home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / shells / rc-1.000 / rc-1 / rc-1.5-linux / lex.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-07  |  10.1 KB  |  396 lines

  1. /* lex.c: rc's lexical analyzer */
  2.  
  3. #include "rc.h"
  4. #include "y.tab.h"
  5.  
  6. /*
  7.     Special characters (i.e., "non-word") in rc:
  8.         \t \n # ; & | ^ $ = ~ ` ' { } @ ! ( ) < > \
  9.  
  10.     The lexical analyzer is fairly straightforward. The only really
  11.     unclean part concerns backslash continuation and "double
  12.     backslashes". A backslash followed by a newline is treated as a
  13.     space, otherwise backslash is not a special character (i.e.,
  14.     it can be part of a word).  This introduces a host of unwanted
  15.     special cases. In our case, \ cannot be a word character, since
  16.     we wish to read in all word characters in a tight loop.
  17.  
  18.     Note: to save the trouble of declaring these arrays with TRUEs
  19.     and FALSEs, I am assuming that FALSE = 0, TRUE = 1. (and so is
  20.     it declared in rc.h)
  21. */
  22.  
  23. #define BUFSIZE ((SIZE_T) 1000)    /*    malloc hates power of 2 buffers? */
  24. #define BUFMAX (8 * BUFSIZE)    /*     How big the buffer can get before we re-allocate the
  25.                     space at BUFSIZE again. Premature optimization? Maybe.
  26.                 */
  27.  
  28. typedef enum wordstates {
  29.     NW, RW, KW /* "nonword", "realword", "keyword" */
  30. } wordstates;
  31.  
  32. static void getpair(int);
  33.  
  34. int lineno;
  35.  
  36. const char nw[] = {
  37.     1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  38.     1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
  39.     1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,
  40.     1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
  41.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  42.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  43.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  44.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  45. };
  46.  
  47. const char dnw[] = {
  48.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  49.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
  50.     1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
  51.     1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
  52.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  53.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  54.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  55.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
  56. };
  57.  
  58. static SIZE_T bufsize = BUFSIZE;
  59. static char *realbuf = NULL;
  60. static bool newline = FALSE;
  61. static bool errset = FALSE;
  62. static bool prerror = FALSE;
  63. static wordstates w = NW;
  64. static int fd_left, fd_right;
  65.  
  66. #define checkfreecaret {if (w != NW) { w = NW; ugchar(c); return '^'; }}
  67.  
  68. enum filedescriptors {
  69.     UNSET = -9, CLOSED = -1
  70. };
  71.  
  72. extern int yylex() {
  73.     static bool dollar = FALSE;
  74.     bool saw_meta = FALSE;
  75.     int c;
  76.     SIZE_T i;            /* The purpose of all these local assignments is to    */
  77.     const char *meta;        /* allow optimizing compilers like gcc to load these    */
  78.     char *buf = realbuf;        /* values into registers. On a sparc this is a        */
  79.     YYSTYPE *y = &yylval;        /* win, in code size *and* execution time        */
  80.     if (errset) {
  81.         errset = FALSE;
  82.         return '\n';
  83.     }
  84.     /* rc variable-names may contain only alnum, '*' and '_', so use dnw if we are scanning one. */
  85.     meta = (dollar ? dnw : nw);
  86.     dollar = FALSE;
  87.     if (newline) {
  88.         --lineno; /* slight space optimization; print_prompt2() always increments lineno */
  89.         print_prompt2();
  90.         newline = FALSE;
  91.     }
  92. top:    while ((c = gchar()) == ' ' || c == '\t')
  93.         w = NW;
  94.     if (c == EOF)
  95.         return END;
  96.     if (!meta[(unsigned char) c]) {    /* it's a word or keyword. */
  97.         checkfreecaret;
  98.         w = RW;
  99.         i = 0;
  100.     read:    do {
  101.             buf[i++] = c;
  102.             if (c == '?' || c == '[' || c == '*')
  103.                 saw_meta = TRUE;
  104.             if (i >= bufsize)
  105.                 buf = realbuf = erealloc(buf, bufsize *= 2);
  106.         } while ((c = gchar()) != EOF && !meta[(unsigned char) c]);
  107.         while (c == '\\') {
  108.             if ((c = gchar()) == '\n') {
  109.                 print_prompt2();
  110.                 c = ' '; /* Pretend a space was read */
  111.                 break;
  112.             } else {
  113.     bs:            if (meta != dnw) { /* all words but varnames may have a bslash */
  114.                     buf[i++] = '\\';
  115.                     if (i >= bufsize)
  116.                         buf = realbuf = erealloc(buf, bufsize *= 2);
  117.                     if (!meta[(unsigned char) c])
  118.                         goto read;
  119.                 } else {
  120.                     ugchar(c);
  121.                     c = '\\';
  122.                     break;
  123.                 }
  124.             }
  125.         }
  126.         ugchar(c);
  127.         buf[i] = '\0';
  128.         w = KW;
  129.         if (i == 2) {
  130.             if (*buf == 'i' && buf[1] == 'f') return IF;
  131.             if (*buf == 'f' && buf[1] == 'n') return FN;
  132.             if (*buf == 'i' && buf[1] == 'n') return IN;
  133.         }
  134.         if (streq(buf, "for")) return FOR;
  135.         if (streq(buf, "else")) return ELSE;
  136.         if (streq(buf, "switch")) return SWITCH;
  137.         if (streq(buf, "while")) return WHILE;
  138.         if (streq(buf, "case")) return CASE;
  139.         w = RW;
  140.         y->word.w = ncpy(buf);
  141.         if (saw_meta) {
  142.             char *r, *s;
  143.  
  144.             y->word.m = nalloc(strlen(buf) + 1);
  145.             for (r = buf, s = y->word.m; *r != '\0'; r++, s++)
  146.                 *s = (*r == '?' || *r == '[' || *r == '*');
  147.         } else {
  148.             y->word.m = NULL;
  149.         }
  150.         return WORD;
  151.     }
  152.     if (c == '`' || c == '!' || c == '@' || c == '~' || c == '$' || c == '\'') {
  153.         checkfreecaret;
  154.         if (c == '!' || c == '@' || c == '~')
  155.             w = KW;
  156.     }
  157.     switch (c) {
  158.     case '!':
  159.         return BANG;
  160.     case '@':
  161.         return SUBSHELL;
  162.     case '~':
  163.         return TWIDDLE;
  164.     case '`':
  165.         c = gchar();
  166.         if (c == '`')
  167.             return BACKBACK;
  168.         ugchar(c);
  169.         return '`';
  170.     case '$':
  171.         dollar = TRUE;
  172.         c = gchar();
  173.         if (c == '#')
  174.             return COUNT;
  175.         if (c == '^')
  176.             return FLAT;
  177.         ugchar(c);
  178.         return '$';
  179.     case '\'':
  180.         w = RW;
  181.         i = 0;
  182.         do {
  183.             buf[i++] = c;
  184.             if (c == '\n')
  185.                 print_prompt2();
  186.             if (c == EOF) {
  187.                 w = NW;
  188.                 scanerror("eof in quoted string");
  189.                 return HUH;
  190.             }
  191.             if (i >= bufsize)
  192.                 buf = realbuf = erealloc(buf, bufsize *= 2);
  193.         } while ((c = gchar()) != '\'' || (c = gchar()) == '\''); /* quote "'" thus: 'how''s it going?' */
  194.         ugchar(c);
  195.         buf[i] = '\0';
  196.         y->word.w = ncpy(buf);
  197.         y->word.m = NULL;
  198.         return WORD;
  199.     case '\\':
  200.         if ((c = gchar()) == '\n') {
  201.             print_prompt2();
  202.             goto top; /* Pretend it was just another space. */
  203.         }
  204.         ugchar(c);
  205.         c = '\\';
  206.         checkfreecaret;
  207.         c = gchar();
  208.         i = 0;
  209.         goto bs;
  210.     case '(':
  211.         if (w == RW) /* SUB's happen only after real words, not keyowrds, so if () and while () work */
  212.             c = SUB;
  213.         w = NW;
  214.         return c;
  215.     case '#':
  216.         while ((c = gchar()) != '\n') /* skip comment until newline */
  217.             if (c == EOF)
  218.                 return END;
  219.         /* FALLTHROUGH */
  220.     case '\n':
  221.         lineno++;
  222.         newline = TRUE;
  223.         /* FALLTHROUGH */
  224.     case ';':
  225.     case '^':
  226.     case ')':
  227.     case '=':
  228.     case '{': case '}':
  229.         w = NW;
  230.         return c;
  231.     case '&':
  232.         w = NW;
  233.         c = gchar();
  234.         if (c == '&')
  235.             return ANDAND;
  236.         ugchar(c);
  237.         return '&';
  238.     case '|':
  239.         w = NW;
  240.         c = gchar();
  241.         if (c == '|')
  242.             return OROR;
  243.         getpair(c);
  244.         if (errset)
  245.             return HUH;
  246.         if ((y->pipe.left = fd_left) == UNSET)
  247.             y->pipe.left = 1;                /* default to fd 1 */
  248.         if ((y->pipe.right = fd_right) == UNSET)
  249.             y->pipe.right = 0;                /* default to fd 0 */
  250.         if (y->pipe.right == CLOSED) {
  251.             scanerror("expected digit after '='");        /* can't close a pipe */
  252.             return HUH;
  253.         }
  254.         return PIPE;
  255.     case '>':
  256.         c = gchar();
  257.         if (c == '>') {
  258.             c = gchar();
  259.             y->redir.type = rAppend;
  260.         } else
  261.             y->redir.type = rCreate;
  262.         y->redir.fd = 1;
  263.         goto common;
  264.     case '<':
  265.         c = gchar();
  266.         if (c == '<') {
  267.             c = gchar();
  268.             if (c == '<') {
  269.                 c = gchar();
  270.                 y->redir.type = rHerestring;
  271.             } else {
  272.                 y->redir.type = rHeredoc;
  273.             }
  274.         } else
  275.             y->redir.type = rFrom;
  276.         y->redir.fd = 0;
  277.     common:
  278.         w = NW;
  279.         getpair(c);
  280.         if (errset)
  281.             return HUH;
  282.         if (fd_right == UNSET) { /* redirection, not dup */
  283.             if (fd_left != UNSET) {
  284.                 y->redir.fd = fd_left;
  285.                 return SREDIR;
  286.             }
  287.             return (y->redir.type == rFrom || y->redir.type == rCreate) ? REDIR : SREDIR;
  288.         } else { /* dup; recast yylval */
  289.             y->dup.type = y->redir.type;
  290.             y->dup.left = fd_left;
  291.             y->dup.right = fd_right;
  292.             return DUP;
  293.         }
  294.     default:
  295.         w = NW;
  296.         return c; /* don't know what it is, let yacc barf on it */
  297.     }
  298. }
  299.  
  300. extern void yyerror(const char *s) {
  301.     char *tok;
  302.     if (prerror) { /* don't print "syntax error" if there's a more informative scanerror */
  303.         prerror = FALSE;
  304.         return;
  305.     }
  306.     if (!interactive) {
  307.         if (w != NW)
  308.             tok = realbuf;
  309.         else if (last == EOF)
  310.             tok = "eof";
  311.         else if (last == '\n')
  312.             tok = "end of line";
  313.         else
  314.             tok = nprint((last < 32 || last > 126) ? "(decimal %d)" : "'%c'", last);
  315.         fprint(2, "line %d: %s near %s\n", lineno - (last == '\n'), s, tok);
  316.     } else
  317.         fprint(2, "%s\n", s);
  318. }
  319.  
  320. extern void scanerror(char *s) {
  321.     flushu(); /* flush upto newline */
  322.     yyerror(s);
  323.     errset = prerror = TRUE;
  324. }
  325.  
  326. extern void inityy() {
  327.     newline = FALSE;
  328.     w = NW;
  329.     hq = NULL;
  330.     /* return memory to the system if the buffer got too large */
  331.     if (bufsize > BUFMAX && realbuf != NULL) {
  332.         efree(realbuf);
  333.         bufsize = BUFSIZE;
  334.         realbuf = ealloc(bufsize);
  335.     } else if (realbuf == NULL)
  336.         realbuf = ealloc(bufsize);
  337. }
  338.  
  339. extern void print_prompt2() {
  340.     lineno++;
  341. #ifdef READLINE
  342.     prompt = prompt2;
  343. #else
  344.     if (interactive)
  345.         fprint(2, "%s", prompt2);
  346. #endif
  347. }
  348.  
  349. /*
  350.    Scan in a pair of integers for redirections like >[2=1]. CLOSED represents a closed file
  351.    descriptor (i.e., >[2=]) and UNSET represents an undesignated file descriptor (e.g.,
  352.    >[2] is represented as (2,UNSET).
  353.  
  354.    This function makes use of unsigned compares to make range tests in one compare operation.
  355. */
  356.  
  357. static void getpair(int c) {
  358.     int n;
  359.     fd_left = fd_right = UNSET;
  360.     if (c != '[') {
  361.         ugchar(c);
  362.         return;
  363.     }
  364.     if ((unsigned int) (n = gchar() - '0') > 9) {
  365.         scanerror("expected digit after '['");
  366.         return;
  367.     }
  368.     while ((unsigned int) (c = gchar() - '0') <= 9)
  369.         n = n * 10 + c;
  370.     fd_left = n;
  371.     c += '0';
  372.     switch (c) {
  373.     default:
  374.         scanerror("expected '=' or ']' after digit");
  375.         return;
  376.     case ']':
  377.         return;
  378.     case '=':
  379.         if ((unsigned int) (n = gchar() - '0') > 9) {
  380.             if (n != ']' - '0') {
  381.                 scanerror("expected digit or ']' after '='");
  382.                 return;
  383.             }
  384.             fd_right = CLOSED;
  385.         } else {
  386.             while ((unsigned int) (c = gchar() - '0') <= 9)
  387.                 n = n * 10 + c;
  388.             if (c != ']' - '0') {
  389.                 scanerror("expected ']' after digit");
  390.                 return;
  391.             }
  392.             fd_right = n;
  393.         }
  394.     }
  395. }
  396.